webpack与grunt、gulp的不同
// grunt和gulp在早期比较流行，属于前端工具类
// grunt和gulp是基于任务和流（Task、Stream）的。类似jQuery，找到一个（或一类）文件，对其做一系列链式操作，更新流上的数据， 
// 整条链式操作构成了一个任务，多个任务就构成了整个web的构建流程

// webpack是基于入口的。会自动地递归解析入口所需要加载的所有资源文件，然后用不同的Loader来处理不同的文件，用Plugin来扩展webpack功能

有哪些常见的Loader
// file-loader：把文件输出到一个文件夹中，在代码中通过相对 URL 去引用输出的文件
// url-loader：和 file-loader 类似，但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
// source-map-loader：加载额外的 Source Map 文件，以方便断点调试
// image-loader：加载并且压缩图片文件
// babel-loader：把 ES6 转换成 ES5
// css-loader：加载 CSS，支持模块化、压缩、文件导入等特性
// style-loader：把 CSS 代码注入到 JavaScript 中，通过 DOM 操作去加载 CSS。
// eslint-loader：通过 ESLint 检查 JavaScript 代码
有哪些常见的Plugin
// define-plugin：定义环境变量
// html-webpack-plugin：简化html文件创建 会在打包结束后，自动生成一个html问件，并把打包生成的js模块引入到该html中。
// uglifyjs-webpack-plugin：通过UglifyES压缩ES6代码
// webpack-parallel-uglify-plugin: 多核压缩,提高压缩速度
// webpack-bundle-analyzer: 可视化webpack输出文件的体积
// mini-css-extract-plugin: CSS提取到单独的文件中,支持按需加载

分别介绍bundle，chunk，module是什么
// bundle：是由webpack打包出来的文件
// chunk：代码块，一个chunk由多个模块组合而成，用于代码的合并和分割
// module：是开发中的单个模块，在webpack的世界，一切皆模块，一个模块对应一个文件，webpack会从配置的entry中递归开始找出所有依赖的模块
// 我们直接写出来的是 module，webpack 处理时是 chunk，最后生成浏览器可以直接运行的 bundle
http://www.hellojava.com/a/90835.html
Loader和Plugin的不同
不同的作用:
// Loader直译为"加载器"。Webpack将一切文件视为模块，但是webpack原生是只能解析js文件，如果想将其他文件也打包的话，就会用到loader。 
    // 所以Loader的作用是让webpack拥有了加载和解析_非JavaScript文件_的能力。
// Plugin直译为"插件"。Plugin可以扩展webpack的功能，让webpack具有更多的灵活性。 在
    //  Webpack 运行的生命周期中会广播出许多事件，Plugin 可以监听这些事件，在合适的时机通过 Webpack 提供的 API 改变输出结果。
不同的用法:
// Loader在module.rules中配置，也就是说他作为模块的解析规则而存在。 类型为数组，每一项都是一个Object，里面描述了对于什么类型的文件（test），使用什么加载(loader)和使用的参数（options）
// Plugin在plugins中单独配置。 类型为数组，每一项是一个plugin的实例，参数都通过构造函数传入

webpack 工程化实践总结之webpack 核心模块、Compiler 和 Compilation、基本流程和 HMR
https://blog.csdn.net/weixin_42614080/article/details/110507675
// Webpack 的运行流程是一个串行的过程，从启动到结束会依次执行以下流程：
// 初始化参数：从配置文件和 Shell 语句中读取与合并参数，得出最终的参数；
// 开始编译：用上一步得到的参数初始化 Compiler 对象，加载所有配置的插件，执行对象的 run 方法开始执行编译；
// 确定入口：根据配置中的 entry 找出所有的入口文件；
// 编译模块：从入口文件出发，调用所有配置的 Loader 对模块进行翻译，再找出该模块依赖的模块，再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理；
// 完成模块编译：在经过第4步使用 Loader 翻译完所有模块后，得到了每个模块被翻译后的最终内容以及它们之间的依赖关系；
// 输出资源：根据入口和模块之间的依赖关系，组装成一个个包含多个模块的 Chunk，再把每个 Chunk 转换成一个单独的文件加入到输出列表，这步是可以修改输出内容的最后机会；
// 输出完成：在确定好输出内容后，根据配置确定输出的路径和文件名，把文件内容写入到文件系统。
// 在以上过程中，Webpack 会在特定的时间点广播出特定的事件，插件在监听到感兴趣的事件后会执行特定的逻辑，并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。

// 用我自己的话说
// webpack执行的过程其实就是一个串行的过程
// 1.初始化参数，从配置文件等拿到需要的参数
// 2.开始编译：这个时候根据这些参数去这个编译程序的实例，加载所有的配置插件
// 3.根据entry 找到文件的入口
// 4.从入口出发，根据配置的loader进行对模块的一个处理，我们知道webpack原生只对js的支持，所以这样通过loader进行翻译，递归查找所有进行处理
// 5.模块编译完成：我们可以得到了最终的模块的内容和他们之间的依赖关系
// 6.输出资源：这些依赖关系和入口模块 组装成一个个的chunk代码块，然后再把这些代码块转换成文件加入输出列表
// 7.进行输出文件：根据配置的路径和文件名进行输出

是否写过Loader和Plugin？描述一下编写loader或plugin的思路
// Loader像一个"翻译官"把读到的源文件内容转义成新的文件内容，并且每个Loader通过链式操作，将源文件一步步翻译成想要的样子。
    // 编写Loader时要遵循单一原则，每个Loader只做一种"转义"工作。 
    // 每个Loader的拿到的是源文件内容（source），可以通过返回值的方式将处理后的内容输出，也可以调用this.callback()方法，将内容返回给webpack。 
    // 还可以通过 this.async()生成一个callback函数，再用这个callback将处理后的内容输出出去。 
    // 此外webpack还为开发者准备了开发loader的工具函数集——loader-utils。
// Plugin的编写就灵活了许多。 webpack在运行的生命周期中会广播出许多事件，Plugin 可以监听这些事件，在合适的时机通过 Webpack 提供的 API 改变输出结果。
https://segmentfault.com/q/1010000021981151
webpack热更原理
// 1.webpack对文件的变化进行一个监听，当他监听到变化的时候会对对应的模块重新编译打包，并且打包后的代码保存到内存中
// 2.devServe通知浏览器端文件发生了变化，在启动devServer的时候会在服务端和浏览器端建立一个webSocket的长连接，然后devServer监听到webpack新文件打包后，将编译打包后的新模块的hash值发送到浏览器端
//  tips:webpack-dev-server 修改了webpack 配置中的 entry 属性，在里面添加了 webpack-dev-client 的代码，这样在最后的 bundle.js 文件中就会有接收 websocket 消息的代码了。
// 3.浏览器端接收到发来的hash，会根据配置文件进行处理，如果是reload就刷新，如果是热更的话就会把这个hash值交给webpack的热更模块去处理
// 4.  如果webpack热更模块监听到有新的hash,就会向服务端请求，返回更新的文件列表和最新的模块代码
// 5.  热更模块会进行一些处理，找到旧模块，删除缓存过期的模块和依赖，添加新的模块
// 6. 热更失败的话执行reload
webpack性能优化
// 压缩代码:删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件， 利用cssnano（css-loader?minimize）来压缩css
// 利用CDN加速: 在构建过程中，将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
// Tree Shaking: 将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现
// Code Splitting: 将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利用浏览器缓存
// 提取公共第三方库:  SplitChunksPlugin插件来进行公共模块抽取,利用浏览器缓存可以长期缓存这些无需频繁变动的公共代码
// 外部扩展(externals): 将不怎么需要更新的第三方库脱离webpack打包，不被打入bundle中，从而减少打包时间,比如jQuery用script标签引入
    // http可以并行加载资源这样就提高了加载的速度
如何提高webpack的打包速度
// happypack: 利用进程并行编译loader,利用缓存来使得 rebuild 更快,遗憾的是作者表示已经不会继续开发此项目,类似的替代者是thread-loader
// 外部扩展(externals): 将不怎么需要更新的第三方库脱离webpack打包，不被打入bundle中，从而减少打包时间,比如jQuery用script标签引入
    // http可以并行加载资源这样就提高了加载的速度
// dll: 采用webpack的 DllPlugin 和 DllReferencePlugin 引入dll，让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间
// 利用缓存:webpack.cache、babel-loader.cacheDirectory、HappyPack.cache都可以利用缓存提高rebuild效率
// 减少路径的查找时间 缩小文件搜索范围: 比如babel-loader插件,如果你的文件仅存在于src中,那么可以include: path.resolve(__dirname, 'src'),
    //当然绝大多数情况下这种操作的提升有限,除非不小心build了node_modules文件
webpack的几种hash的区别
具体的hash有三种
// 1、Hash
// hash是跟整个项目的构建相关，只要项目里有文件更改，整个项目构建的hash值都会更改，并且全部文件都共用相同的hash值
// 2、chunkhash
// chunkhash，它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk，生成对应的哈希值。
// 简单来说这种是根据不同入口来配置的，比如vue-router、vuex、vue等公共入口文件，只要这些没有改变，那么他对应生成的js的hash值也不会改变。
// 3、contenthash
// contenthash主要是处理关联性，比如一个js文件中引入css，但是会生成一个js文件，一个css文件，但是因为入口是一个，导致他们的hash值也相同，所以当只有js修改时，
    // 关联输出的css、img等文件的hash值也会改变，这种情况下就需要contenthash了